#include <pthread.h>
#include <sys/time.h>

namespace glf {

class SyncEvent
	: private NonCopyable
#if GLF_DEBUG
	, public SyncEventConcept
#endif
{
public:

	SyncEvent()
		: m_State(STATE_NONE)
		, m_Initialized(false) {
		Init();
	}

	SyncEvent(DeferEvent)
		: m_State(STATE_NONE)
		, m_Initialized(false) {
	}

	void Init() {
		GLF_ASSERT(m_Initialized == false);

		pthread_mutex_init(&m_Mutex, NULL);
		pthread_cond_init(&m_Condition, NULL);

		m_Initialized = true;
	}

	~SyncEvent() {
		if (m_Initialized)
		{
			pthread_mutex_destroy(&m_Mutex);
			pthread_cond_destroy(&m_Condition);
		}
	}

	void Signal() {
		GLF_ASSERT(m_Initialized);

		pthread_mutex_lock(&m_Mutex);
		if (m_State & STATE_WAIT)
		{
			pthread_cond_signal(&m_Condition);
		}
		else
		{
			m_State |= STATE_SIGNALED;
		}
		pthread_mutex_unlock(&m_Mutex);
	}

	void Set() {
		GLF_ASSERT(m_Initialized);

		pthread_mutex_lock(&m_Mutex);
		m_State |= STATE_PERMANENT;
		pthread_cond_broadcast(&m_Condition);
		pthread_mutex_unlock(&m_Mutex);
	}

	void Reset() {
		GLF_ASSERT(m_Initialized);

		pthread_mutex_lock(&m_Mutex);
		m_State &= ~STATE_PERMANENT;
		pthread_mutex_unlock(&m_Mutex);
	}

	bool Wait(unsigned long duration = 0) {
		GLF_ASSERT(m_Initialized);

		pthread_mutex_lock(&m_Mutex);

		if (m_State & STATE_PERMANENT)
		{
			pthread_mutex_unlock(&m_Mutex);
			return true;
		}

		if (m_State & STATE_SIGNALED)
		{
			m_State &= ~STATE_SIGNALED;
			pthread_mutex_unlock(&m_Mutex);
			return true;
		}

		m_State |= STATE_WAIT;

		if (duration == 0)
		{
			bool success = pthread_cond_wait(&m_Condition, &m_Mutex) == 0;
			m_State &= ~STATE_WAIT;
			pthread_mutex_unlock(&m_Mutex);
			return success;
		}
		else
		{
			timespec ts;
			timeval  tp;

			gettimeofday(&tp, NULL);

			size_t secAdj	= duration / 1000;
			size_t usecAdj	= (duration % 1000) * 1000;

			ts.tv_sec  = tp.tv_sec  + secAdj;
            ts.tv_nsec = tp.tv_usec + usecAdj;

			bool success = pthread_cond_timedwait(&m_Condition, &m_Mutex, &ts) == 0;
			m_State &= ~STATE_WAIT;
			pthread_mutex_unlock(&m_Mutex);
			return success;
		}
	}

private:

	pthread_mutex_t m_Mutex;
	pthread_cond_t  m_Condition;

	static const unsigned long STATE_NONE      = 0x00;
	static const unsigned long STATE_PERMANENT = 0x01;
	static const unsigned long STATE_WAIT      = 0x02;
	static const unsigned long STATE_SIGNALED  = 0x04;

	unsigned long m_State;
	bool m_Initialized;

};

} // namespace glf
